#ifndef TIMEPOINTMAP_H
#define	TIMEPOINTMAP_H
#include "stdafx.h"
//#include "timestamp.h"
//#include "Tempmap.h"
//#include "Basics_vector.h"
//#include "Basics.h"

using namespace std;
template <class T> class timepointmap
{

    vector <timestamp> time;
    vector <T> val;
	T nodata;
	T nulldata;
	int set_replace (unsigned int row,const timestamp &ts,const T value)
	{
		this->time[row]=ts;
		this->val[row]=value;
		return check_myself();
	}
	int set_true_insert(timestamp ts,T value)
	{
		unsigned int pre=Basics_vector::find_precursor_pos_in_ordered_vector(this->time,ts);
		unsigned int suc=Basics_vector::find_successor_pos_in_ordered_vector(this->time,ts);
		//ts is smaller than any time
		if((pre==time.size())&&(suc==0))
		{
			this->set(0,ts,value);
			return this->check_myself();
		}
		//ts is bigger than any time
		if((suc==time.size())&&(pre==time.size()-1))
		{
			this->set(time.size(),ts,value);
			return check_myself();
		}
		//tt is in between
		if((suc<time.size()-1)&&(pre+1==suc))
		{
			this->set(suc,ts,value);
			return check_myself();
		}	
		err::err3("	timepointmap::set_insert: entry can not be inserted or used to replace");
		return 1;
		
	}
public:
        timepointmap()
	{
              
	}
	timepointmap(T inulldata, T inodata)
	{
                nulldata=inulldata;
		nodata=inodata;
	}
        timepointmap(tempmap <string,T> *source)
        {
            nulldata=source->get_nulldata();
            nodata=source->get_nodata();
        }
        
        timepointmap& operator=(const timepointmap &copy_me)
		  {
				this->nodata=copy_me.nodata;
                                 this->nulldata=copy_me.nulldata;
                                 this->time=copy_me.time;
                                this->val=copy_me.val;
				return *this;
		  }
        timepointmap (const timepointmap <T> &copy_me)
        {
            this->nodata=copy_me.nodata;
            this->nulldata=copy_me.nulldata;
            this->time=copy_me.time;
            this->val=copy_me.val;
        }
	int push_back(timestamp ts,T x)
	{
		time.push_back(ts);
		val.push_back(x);
		return 0;
	}
	
	T get_mean_linear_interpolated(int a,int b)
	{
		T mean;
		double tempdiff;
		 mean=this->get_integral_linear_interpolated(a,b);
		 if(mean==nodata) return nodata;
		 tempdiff=this->time[a].get_dist(this->time[b-1]);
		 mean=mean/tempdiff;
		 return mean;
	}
	T get_integral_linear_interpolated(unsigned int a,unsigned int b)
	{
		int dec=0;
		unsigned int row;
		T sum=nulldata;
		double tempdiff;
		double interval;
		this->check_myself();
		if(Basics_vector::count(this->val,a,b,nodata)) dec++;
		if(a>=b) dec++;
		if(dec>0) return nodata;
		for(row=a+1;row<b;row++){
			tempdiff=this->time[row-1].get_dist(this->time[row]);
			interval=Basics_math::mean_(val[row-1],val[row]);
			sum=sum+tempdiff*interval;
		}
		return sum;
	}
	unsigned int get_size(){
		return this->time.size();
	}
	int check_myself()
	{
		// same size of time and val
		if(this->time.size()!=this->time.size()) err::err3("timepointmap::check_myself: sizes of time and val vector are not similar.");
		// are there similar timestampvalues
		if(Basics_vector::check_for_similarities(this->time))err::err3("timepointmap::check_myself: At least two identical time stamp values");
		// right order ?
		if(Basics_vector::check_for_ascending_order(this->time)) err::err3("timepointmap::check_myself: No ascending order of timestamps");
		return 0;
	}
	int set(unsigned int row,const timestamp &ts,const T value)	 
	{
		if (row>time.size()) {
			err::err3("timepointmap::set: row is big");
			return 1;
		}
                if(row<time.size()){
                        this->time.insert(this->time.begin()+row,ts);
                        this->val.insert(this->val.begin()+row,value);
                }
                else{
                    this->time.push_back(ts);
                    this->val.push_back(value);
                }
                        
                
		return check_myself();
	}
        
	int set_insert_or_replace_entry(const timestamp &ts,const T value)
	{
		unsigned int row;
		vector <timestamp>::const_iterator it;
		
		if (this->time.size()==0) this->set(0,ts,value);
		else{
				row=Basics_vector::find_position_of_object(time,ts);
				if(row<this->time.size())
				{
					this->set_replace(row,ts,value);
				}
				else
				{
					this->set_true_insert(ts,value);
				}
				
		}
		return check_myself();
	}
        timestamp get_start(){
            if (this->get_size()>0)
            {
                return this->time[0];
            }
            err::err2("timepointmap::get_start: This timepointmap must be empty.");
        }
        timestamp get_end(){
            if (this->get_size()>0)
            {
                return this->time[this->get_size()-1];
            }
            err::err2("timepointmap::get_start: This timepointmap must be empty.");
        }
	T get_val(unsigned int row)
	{
		if (row>=this->get_size())	{
			err::err3("timepointmap::get_val: row to big for this timepointmap");
			return nodata;
		}
		return this->val[row];
			
	}
        T get_val(const timestamp &ts)
        {
           this->get_val( this->get_row(ts));
        }
        T get_val_lin(const timestamp &ts)
        {
            int start;
            int end;
            if (this->get_row(ts)<this->get_size())
            {
                return this->get_val(ts);
            }
            else
            {
                if((this->get_start()<ts)&&(this->get_end()>ts))
                {
                    start=Basics_vector::find_precursor_pos_in_ordered_vector(this->time,ts);
                    end=Basics_vector::find_successor_pos_in_ordered_vector(this->time,ts);
                    //return this->get_val(start)+this->get_start().get_dist(this->get_timestamp(end))/(this->get_val(end)-this->get_val(start));
                    return this->get_val(start)+
                            this->get_timestamp(start).get_dist(ts)*(this->get_val(end)-this->get_val(start)) 
                            /this->get_timestamp(start).get_dist(this->get_timestamp(end));
                }
            }
            err::err3("tempointmap::get_val_lin: could not produce any output");
            return 1;
        }
	int set_delete_entries(unsigned int start,unsigned int end)
	{
            if (start>end) {
                    err::err3("timepointmap::set_delete_entries:start>end");
                    return 1;
            }
            if(end>(this->get_size()-1)) {
                    err::err3("timepointmap::set_delete_entries:end bigger than timepointmap");
                    return 1;
            }
            this->time.erase(this->time.begin()+start,time.begin()+1+end);
            this->val.erase(this->val.begin()+start,this->val.begin()+1+end);
            return this->check_myself();
	}
	unsigned int get_row(timestamp ts)
	{
		return Basics_vector::find_position_of_object(this->time,ts);
	}
	vector <timestamp>* point_time()
	{
		return &time;
	}
	timestamp get_timestamp(unsigned int row)
	{
		if (row>=this->time.size())
			err::err3("timepointmap::get_timestamp: row outside timepointmap");
		return this->time[row];
	}
};
#endif